{{!

  Copyright (c) Meta Platforms, Inc. and affiliates.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

}}
{{> lib/client_trait}}

/// Client definitions for `{{service:rust_name}}`.
pub struct {{service:rust_name}}Impl<P, T, S = ::fbthrift::NoopSpawner> {
    {{#service:interaction?}}
    #[allow(dead_code)]
    names: &'static {{service:rust_name}}Names,
    {{/service:interaction?}}
    {{#service:extends}}
    parent: {{service:client_package}}::{{service:rust_name}}Impl<P, T, S>,
    {{/service:extends}}
    {{^service:extends}}
    transport: T,
    _phantom: ::std::marker::PhantomData<fn() -> (P, S)>,
    {{/service:extends}}
}

{{#service:interaction?}}
pub struct {{service:rust_name}}Names {
    pub service: &'static ::std::ffi::CStr,
    {{#service:rustFunctions}}
    pub method_{{function:name}}: &'static ::std::ffi::CStr,
    {{/service:rustFunctions}}
}
{{/service:interaction?}}

impl<P, T, S> {{service:rust_name}}Impl<P, T, S>
where
    P: ::fbthrift::Protocol,
    T: ::fbthrift::Transport,
    {{! require P::Frame and T to have compatible DecBuf and EncBuf::Final }}
    P::Frame: ::fbthrift::Framing<DecBuf = ::fbthrift::FramingDecoded<T>>,
    ::fbthrift::ProtocolEncoded<P>: ::fbthrift::BufMutExt<Final = ::fbthrift::FramingEncodedFinal<T>>,
    P::Deserializer: ::std::marker::Send,
    S: ::fbthrift::help::Spawner,
{
    pub fn new(
        transport: T,
        {{#service:interaction?}}
        names: &'static {{service:rust_name}}Names,
        {{/service:interaction?}}
    ) -> Self {
        Self {
            {{#service:interaction?}}
            names,
            {{/service:interaction?}}
            {{#service:extends}}
            parent: {{service:client_package}}::{{service:rust_name}}Impl::<P, T, S>::new(transport),
            {{/service:extends}}
            {{^service:extends}}
            transport,
            _phantom: ::std::marker::PhantomData,
            {{/service:extends}}
        }
    }

    pub fn transport(&self) -> &T {
        ::fbthrift::help::GetTransport::transport(self)
    }

    {{#service:interactions}}
    const NAMES_{{service:rust_name}}: {{service:client_package}}::{{service:rust_name}}Names = {{service:client_package}}::{{service:rust_name}}Names {
        service: c"{{service:parent_service_name}}",
        {{#service:rustFunctions}}
        method_{{function:name}}: c"{{service:parent_service_name}}.{{service:name}}.{{function:name}}",
        {{/service:rustFunctions}}
    };
    {{/service:interactions}}

    {{#service:rustFunctions}}
    {{^function:starts_interaction?}}

    fn _{{function:name}}_impl(
        &self,{{!
        }}{{#function:args}}
        arg_{{field:name}}: {{> lib/arg}},{{!
        }}{{/function:args}}
        rpc_options: T::RpcOptions,
    ) -> {{> lib/client_return_type}} {
        use ::tracing::Instrument as _;
        use ::futures::FutureExt as _;
        {{#function:stream?}}
        use ::futures::StreamExt as _;
        use ::fbthrift::Deserialize as _;
        {{/function:stream?}}
        {{#function:sink?}}
        use ::futures::StreamExt as _;
        use ::fbthrift::Deserialize as _;
        use ::futures::TryFutureExt as _;
        {{/function:sink?}}

        let service_name = {{!
            }}{{^service:interaction?}}c"{{service:name}}"{{/service:interaction?}}{{!
            }}{{#service:interaction?}}self.names.service{{/service:interaction?}}{{!
        }};
        let service_method_name = {{!
            }}{{^service:interaction?}}c"{{service:name}}.{{function:name}}"{{/service:interaction?}}{{!
            }}{{#service:interaction?}}self.names.method_{{function:name}}{{/service:interaction?}}{{!
        }};

        {{#function:creates_interaction?}}
        const INTERACTION_NAME: &::std::ffi::CStr = c"{{function:interaction_name}}";
        {{/function:creates_interaction?}}
        let args = self::Args_{{service:rust_name}}_{{function:name}} {
            {{#function:args}}
            {{field:rust_name}}: arg_{{field:name}},
            {{/function:args}}
            _phantom: ::std::marker::PhantomData,
        };

        {{#function:creates_interaction?}}
        let interaction_transport = match self.transport().create_interaction(INTERACTION_NAME) {
            ::std::result::Result::Ok(res) => res,
            ::std::result::Result::Err(err) => return ::futures::future::err(err.into()).boxed(),
        };
        let interaction_impl = {{program:crate}}::{{function:interaction_name}}Impl::<P, T, S>::new(interaction_transport, &Self::NAMES_{{function:interaction_name}});
        let transport = interaction_impl.transport();
        {{/function:creates_interaction?}}{{^function:creates_interaction?}}
        let transport = self.transport();
        {{/function:creates_interaction?}}

        // need to do call setup outside of async block because T: Transport isn't Send
        let request_env = match ::fbthrift::help::serialize_request_envelope::<P, _>("{{#service:interaction?}}{{service:name}}.{{/service:interaction?}}{{function:name}}", &args) {
            ::std::result::Result::Ok(res) => res,
            ::std::result::Result::Err(err) => return ::futures::future::err(err.into()).boxed(),
        };

        {{#function:stream?}}
        let call_stream = transport
            .call_stream(service_name, service_method_name, request_env, rpc_options)
            .instrument(::tracing::trace_span!("call_stream", method = "{{service:name}}.{{function:name}}"));
        {{/function:stream?}}{{#function:sink?}}
        let call_sink = transport
            .call_sink(service_name, service_method_name, request_env, rpc_options)
            .instrument(::tracing::trace_span!("call_sink", method = "{{service:name}}.{{function:name}}"));
        {{/function:sink?}}{{^function:sink?}}{{^function:stream?}}
        let call = transport
            .call(service_name, service_method_name, request_env, rpc_options)
            .instrument(::tracing::trace_span!("call", method = "{{service:name}}.{{function:name}}"));
        {{/function:stream?}}{{/function:sink?}}

        async move {
            {{#function:stream?}}{{!

            ******************************
            ******* STREAMING BODY *******
            ******************************

            }}
            let (initial, stream) = call_stream.await?;

            let new_stream = stream.then(|item_res| {
                async move {
                    match item_res {
                        ::std::result::Result::Err(err) =>
                            ::std::result::Result::Err({{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}StreamError::from(err)),
                        ::std::result::Result::Ok(item_enc) => {
                            S::spawn(move || {
                                match item_enc {
                                    ::fbthrift::ClientStreamElement::Reply(payload) => {
                                        let mut de = P::deserializer(payload);
                                        <{{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}StreamReader as ::fbthrift::help::DeserializeExn>::read_result(&mut de)
                                    }
                                    ::fbthrift::ClientStreamElement::ApplicationEx(payload) => {
                                        let mut de = P::deserializer(payload);
                                        let aexn = ::fbthrift::ApplicationException::rs_thrift_read(&mut de)?;
                                        ::std::result::Result::Ok(::std::result::Result::Err({{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}StreamError::ApplicationException(aexn)))
                                    }
                                    ::fbthrift::ClientStreamElement::DeclaredEx(payload) => {
                                        let mut de = P::deserializer(payload);
                                        <{{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}StreamReader as ::fbthrift::help::DeserializeExn>::read_result(&mut de)
                                    }
                                }
                            }).await.map_err(::anyhow::Error::from)??
                        }
                    }
                }
            })
            .boxed();

            let de = P::deserializer(initial);
            let res = ::fbthrift::help::async_deserialize_response_envelope::<P, {{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}Reader, S>(de)
                .await??
                .map({{!
                    }}{{#function:stream_has_first_response?}}move |initial| (initial, new_stream){{/function:stream_has_first_response?}}{{!
                    }}{{^function:stream_has_first_response?}}move |_| new_stream{{/function:stream_has_first_response?}}{{!
                }});
            {{/function:stream?}}{{#function:sink?}}{{!

            ******************************
            **** SINK BODY ****
            ******************************

            }}
            let ::fbthrift::SinkReply {
                initial_response: _initial_response,
                sink_processor,
            } = call_sink.await?;

            {{#function:sink_has_first_response?}}
            let de = P::deserializer(_initial_response);
            let res = ::fbthrift::help::async_deserialize_response_envelope::<P, {{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}Reader, S>(de).await?;

            let initial_response = match res {
                ::std::result::Result::Ok(res) => res,
                ::std::result::Result::Err(aexn) => {
                    ::std::result::Result::Err({{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}Error::ApplicationException(aexn))
                }
            }?;
            {{/function:sink_has_first_response?}}{{^function:sink_has_first_response?}}
            let initial_response = ();
            {{/function:sink_has_first_response?}}

            let sink_helper_fn = move |stream: ::futures::stream::BoxStream<'static, ::std::result::Result<
                {{#function:sink_elem_type}}{{> lib/type}}{{/function:sink_elem_type}},
                {{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}SinkError
            >>| {
                {{!TODO (seanlawlor): This decoding should be done on the blocking thread, not the IO thread. }}
                let new_stream = stream.map(|item| {
                    match item {
                        ::std::result::Result::Ok(res) => {
                            let enc = ::fbthrift::help::serialize_stream_item::<P, {{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}SinkError>(::std::result::Result::Ok(res), "{{#service:interaction?}}{{service:name}}.{{/service:interaction?}}{{function:name}}");
                            ::fbthrift::ClientStreamElement::Reply(enc)
                        }
                        ::std::result::Result::Err({{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}SinkError::ApplicationException(aexn)) => {
                            let enc = ::fbthrift::help::serialize_stream_item::<P, {{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}SinkError>(::std::result::Result::Err({{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}SinkError::ApplicationException(aexn)), "{{#service:interaction?}}{{service:name}}.{{/service:interaction?}}{{function:name}}");
                            ::fbthrift::ClientStreamElement::ApplicationEx(enc)
                        }
                        ::std::result::Result::Err({{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}SinkError::ThriftError(err)) => {
                            let enc = ::fbthrift::help::serialize_stream_item::<P, {{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}SinkError>(::std::result::Result::Err({{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}SinkError::ThriftError(err)), "{{#service:interaction?}}{{service:name}}.{{/service:interaction?}}{{function:name}}");
                            ::fbthrift::ClientStreamElement::ApplicationEx(enc)
                        }
                        {{#if (not (array.empty? function:sink.exceptions))}}
                        ::std::result::Result::Err(other_err) => {
                            let enc = ::fbthrift::help::serialize_stream_item::<P, {{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}SinkError>(::std::result::Result::Err(other_err), "{{#service:interaction?}}{{service:name}}.{{/service:interaction?}}{{function:name}}");
                            ::fbthrift::ClientStreamElement::DeclaredEx(enc)
                        }
                        {{/if (not (array.empty? function:sink.exceptions))}}
                    }
                }).boxed();
                let sinker = sink_processor;
                async move {
                    let payload = (sinker)(new_stream).await?;
                    S::spawn(move || {
                        let mut de = P::deserializer(payload);
                        <{{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}SinkFinalReader as ::fbthrift::help::DeserializeExn>::read_result(&mut de)
                    }).await.map_err(::anyhow::Error::from)??
                }.boxed()
            };

            let res = Ok(::fbthrift::SinkResult::<
                {{#function:sink_has_first_response?}}{{#function:return_type}}{{#function:sink_first_response_type}}{{> lib/type}}{{/function:sink_first_response_type}}{{/function:return_type}}{{/function:sink_has_first_response?}}{{^function:sink_has_first_response?}}(){{/function:sink_has_first_response?}},
                {{#function:sink_elem_type}}{{> lib/type}}{{/function:sink_elem_type}},
                {{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}SinkError,
                {{#function:sink_final_response_type}}{{> lib/type}}{{/function:sink_final_response_type}},
                {{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}SinkFinalError,
            > {
                initial_response,
                sink: Box::new(sink_helper_fn),
            });
            {{/function:sink?}}{{^function:sink?}}{{^function:stream?}}{{!

            ******************************
            **** SINGLE RESPONSE BODY ****
            ******************************

            }}
            let reply_env = call.await?;

            let de = P::deserializer(reply_env);
            let res = ::fbthrift::help::async_deserialize_response_envelope::<P, {{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}Reader, S>(de).await?;

            let res = match res {
                ::std::result::Result::Ok(res) => res,
                ::std::result::Result::Err(aexn) => {
                    ::std::result::Result::Err({{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}Error::ApplicationException(aexn))
                }
            };
            {{/function:stream?}}{{/function:sink?}}
            {{#function:return_type}}{{#function:creates_interaction?}}{{!

            ***********************************
            **** Add interaction to result ****
            ***********************************

            }}
            let interaction_client: {{program:crate}}::client::{{function:interaction_name}}Client = ::std::sync::Arc::new(interaction_impl);
            {{#function:stream?}}
            ::std::result::Result::Ok((interaction_client, res?))
            {{/function:stream?}}{{^function:stream?}}
            {{#type:void?}}
            res?;
            ::std::result::Result::Ok(interaction_client)
            {{/type:void?}}{{^type:void?}}
            ::std::result::Result::Ok((interaction_client, res?))
            {{/type:void?}}{{/function:stream?}}{{!
            }}{{/function:creates_interaction?}}{{^function:creates_interaction?}}
            res
            {{/function:creates_interaction?}}{{/function:return_type}}
        }
        .instrument(::tracing::info_span!("stream", method = "{{service:name}}.{{function:name}}"))
        .boxed()
    }
    {{/function:starts_interaction?}}
    {{/service:rustFunctions}}
}

impl<P, T, S> ::fbthrift::help::GetTransport<T> for {{service:rust_name}}Impl<P, T, S>
where
    T: ::fbthrift::Transport,
{
    fn transport(&self) -> &T {
        {{#service:extends}}
        self.parent.transport()
        {{/service:extends}}
        {{^service:extends}}
        &self.transport
        {{/service:extends}}
    }
}

{{#service:extendedClients}}
#[allow(deprecated)]{{! `dependencies` modules are deprecated other than for packagePrefix to use }}
impl<'a, P, T, S> {{!
    }}{{#extendedService:service}}{{!
    }}::std::convert::AsRef<dyn {{extendedService:packagePrefix}}::{{service:rust_name}} + 'a> {{!
    }}{{/extendedService:service}}{{!
    }}for {{service:rust_name}}Impl<P, T, S>
where
    P: ::fbthrift::Protocol,
    T: ::fbthrift::Transport,
    {{! require P::Frame and T to have compatible DecBuf and EncBuf::Final }}
    P::Frame: ::fbthrift::Framing<DecBuf = ::fbthrift::FramingDecoded<T>>,
    ::fbthrift::ProtocolEncoded<P>: ::fbthrift::BufMutExt<Final = ::fbthrift::FramingEncodedFinal<T>>,
    P::Deserializer: ::std::marker::Send,
    S: ::fbthrift::help::Spawner,
{
    fn as_ref(&self) -> &(dyn {{#extendedService:service}}{{!
        }}{{extendedService:packagePrefix}}::{{service:rust_name}}{{!
        }}{{/extendedService:service}}{{!
        }} + 'a)
    {
        {{extendedService:asRefImpl}}
    }
}

#[allow(deprecated)]{{! `dependencies` modules are deprecated other than for packagePrefix to use }}
impl<'a, P, T, S> {{!
    }}{{#extendedService:service}}{{!
    }}::std::convert::AsRef<dyn {{extendedService:packagePrefix}}::{{service:rust_name}}Ext<T> + 'a> {{!
    }}{{/extendedService:service}}{{!
    }}for {{service:rust_name}}Impl<P, T, S>
where
    P: ::fbthrift::Protocol,
    T: ::fbthrift::Transport,
    {{! require P::Frame and T to have compatible DecBuf and EncBuf::Final }}
    P::Frame: ::fbthrift::Framing<DecBuf = ::fbthrift::FramingDecoded<T>>,
    ::fbthrift::ProtocolEncoded<P>: ::fbthrift::BufMutExt<Final = ::fbthrift::FramingEncodedFinal<T>>,
    P::Deserializer: ::std::marker::Send,
    S: ::fbthrift::help::Spawner,
{
    fn as_ref(&self) -> &(dyn {{#extendedService:service}}{{!
        }}{{extendedService:packagePrefix}}::{{service:rust_name}}Ext<T>{{!
        }}{{/extendedService:service}}{{!
        }} + 'a)
    {
        {{extendedService:asRefImpl}}
    }
}

{{/service:extendedClients}}

{{#service:rustFunctions}}{{^function:starts_interaction?}}

struct Args_{{service:rust_name}}_{{function:name}}<'a> {
    {{#function:args}}
    {{field:rust_name}}: {{> lib/arg_life}},
    {{/function:args}}
    _phantom: ::std::marker::PhantomData<&'a ()>,
}

impl<'a, P: ::fbthrift::ProtocolWriter> ::fbthrift::Serialize<P> for self::Args_{{service:rust_name}}_{{function:name}}<'a> {
    #[inline]{{! No cost because there's only one caller; with luck will mitigate move cost of args. }}
    #[::tracing::instrument(skip_all, level = "trace", name = "serialize_args", fields(method = "{{service:name}}.{{function:name}}"))]
    fn rs_thrift_write(&self, p: &mut P) {
        p.write_struct_begin("args");{{!
        }}{{#function:args}}
        p.write_field_begin({{!
            }}"{{field:name}}", {{!
            }}{{#field:type}}{{> lib/ttype}}{{/field:type}}, {{!
            }}{{field:id}}i16{{!
        }});
        {{#field:type}}{{!
          }}{{#type:has_adapter?}}{{!
            }}::fbthrift::Serialize::rs_thrift_write(&{{> lib/adapter/qualified}}::to_thrift_field::<fbthrift::metadata::NoThriftAnnotations>(&self.{{field:rust_name}}, {{field:id}}), p){{!
          }}{{/type:has_adapter?}}{{!
          }}{{^type:has_adapter?}}{{!
            }}{{> lib/write}}(&self.{{field:rust_name}}, p){{!
          }}{{/type:has_adapter?}}{{!
        }}{{/field:type}};
        p.write_field_end();{{!
        }}{{/function:args}}
        p.write_field_stop();
        p.write_struct_end();
    }
}
{{/function:starts_interaction?}}{{/service:rustFunctions}}

impl<P, T, S> {{service:rust_name}} for {{service:rust_name}}Impl<P, T, S>
where
    P: ::fbthrift::Protocol,
    T: ::fbthrift::Transport,
    {{! require P::Frame and T to have compatible DecBuf and EncBuf::Final }}
    P::Frame: ::fbthrift::Framing<DecBuf = ::fbthrift::FramingDecoded<T>>,
    ::fbthrift::ProtocolEncoded<P>: ::fbthrift::BufMutExt<Final = ::fbthrift::FramingEncodedFinal<T>>,
    P::Deserializer: ::std::marker::Send,
    S: ::fbthrift::help::Spawner,
{{> lib/block}}

    {{#service:rustFunctions}}
    {{^function:starts_interaction?}}
    fn {{function:rust_name}}(
        &self,{{!
        }}{{#function:args}}
        arg_{{field:name}}: {{> lib/arg}},{{!
        }}{{/function:args}}
    ) -> {{> lib/client_return_type}} {
        let rpc_options = T::RpcOptions::default();
        self._{{function:name}}_impl(
            {{#function:args}}
            arg_{{field:name}},
            {{/function:args}}
            rpc_options,
        )
    }
    {{/function:starts_interaction?}}{{#function:starts_interaction?}}

    fn {{function:name}}(
        &self,
    ) -> ::std::result::Result<{{!
    }}{{program:crate}}::client::{{function:interaction_name}}Client, {{!
    }}::anyhow::Error> {
        ::std::result::Result::Ok(
            ::std::sync::Arc::new(
                {{program:crate}}::client::{{function:interaction_name}}Impl::<P, T, S>::new(
                    self.transport().create_interaction(c"{{function:interaction_name}}")?,
                    &Self::NAMES_{{function:interaction_name}},
                )
            )
        )
    }
    {{/function:starts_interaction?}}{{/service:rustFunctions}}
}

impl<P, T, S> {{service:rust_name}}Ext<T> for {{service:rust_name}}Impl<P, T, S>
where
    P: ::fbthrift::Protocol,
    T: ::fbthrift::Transport,
    {{! require P::Frame and T to have compatible DecBuf and EncBuf::Final }}
    P::Frame: ::fbthrift::Framing<DecBuf = ::fbthrift::FramingDecoded<T>>,
    ::fbthrift::ProtocolEncoded<P>: ::fbthrift::BufMutExt<Final = ::fbthrift::FramingEncodedFinal<T>>,
    P::Deserializer: ::std::marker::Send,
    S: ::fbthrift::help::Spawner,
{{> lib/block}}

    {{#service:rustFunctions}}
    {{^function:starts_interaction?}}
    fn {{function:rust_name}}_with_rpc_opts(
        &self,{{!
        }}{{#function:args}}
        arg_{{field:name}}: {{> lib/arg}},{{!
        }}{{/function:args}}
        rpc_options: T::RpcOptions,
    ) -> {{> lib/client_return_type}} {
        self._{{function:name}}_impl(
            {{#function:args}}
            arg_{{field:name}},
            {{/function:args}}
            rpc_options,
        )
    }
    {{/function:starts_interaction?}}
    {{/service:rustFunctions}}
    {{^service:extendedClients}}

    fn transport(&self) -> &T {
        self.transport()
    }
    {{/service:extendedClients}}
}

{{#service:interaction?}}
pub type {{service:rust_name}}DynClient = dyn {{service:rust_name}} + ::std::marker::Send + ::std::marker::Sync + 'static;
pub type {{service:rust_name}}Client = ::std::sync::Arc<{{service:rust_name}}DynClient>;

{{/service:interaction?}}
{{^service:interaction?}}
#[derive(Clone)]
pub struct make_{{service:rust_name}};

/// To be called by user directly setting up a client. Avoids
/// needing ClientFactory trait in scope, avoids unidiomatic
/// make_Trait name.
///
/// ```
/// # const _: &str = stringify! {
/// use bgs::client::BuckGraphService;
///
/// let protocol = BinaryProtocol::new();
/// let transport = HttpClient::new();
/// let client = <dyn BuckGraphService>::new(protocol, transport);
/// # };
/// ```
impl dyn {{service:rust_name}} {
    {{#service:annotations}}
    {{#annotation:value?}}
    pub const {{annotation:rust_name}}: &'static ::std::primitive::str = {{annotation:rust_value}};
    {{/annotation:value?}}
    {{/service:annotations}}
    pub fn new<P, T>(
        protocol: P,
        transport: T,
    ) -> ::std::sync::Arc<impl {{service:rust_name}} + ::std::marker::Send + ::std::marker::Sync + 'static>
    where
        P: ::fbthrift::Protocol<Frame = T>,
        T: ::fbthrift::Transport,
        P::Deserializer: ::std::marker::Send,
    {
        let spawner = ::fbthrift::help::NoopSpawner;
        Self::with_spawner(protocol, transport, spawner)
    }

    pub fn with_spawner<P, T, S>(
        protocol: P,
        transport: T,
        spawner: S,
    ) -> ::std::sync::Arc<impl {{service:rust_name}} + ::std::marker::Send + ::std::marker::Sync + 'static>
    where
        P: ::fbthrift::Protocol<Frame = T>,
        T: ::fbthrift::Transport,
        P::Deserializer: ::std::marker::Send,
        S: ::fbthrift::help::Spawner,
    {
        let _ = protocol;
        let _ = spawner;
        ::std::sync::Arc::new({{service:rust_name}}Impl::<P, T, S>::new(transport))
    }
}

impl<T> dyn {{service:rust_name}}Ext<T>
where
    T: ::fbthrift::Transport,
{
    pub fn new<P>(
        protocol: P,
        transport: T,
    ) -> ::std::sync::Arc<impl {{service:rust_name}}Ext<T> + ::std::marker::Send + ::std::marker::Sync + 'static>
    where
        P: ::fbthrift::Protocol<Frame = T>,
        P::Deserializer: ::std::marker::Send,
    {
        let spawner = ::fbthrift::help::NoopSpawner;
        Self::with_spawner(protocol, transport, spawner)
    }

    pub fn with_spawner<P, S>(
        protocol: P,
        transport: T,
        spawner: S,
    ) -> ::std::sync::Arc<impl {{service:rust_name}}Ext<T> + ::std::marker::Send + ::std::marker::Sync + 'static>
    where
        P: ::fbthrift::Protocol<Frame = T>,
        P::Deserializer: ::std::marker::Send,
        S: ::fbthrift::help::Spawner,
    {
        let _ = protocol;
        let _ = spawner;
        ::std::sync::Arc::new({{service:rust_name}}Impl::<P, T, S>::new(transport))
    }
}

pub type {{service:rust_name}}DynClient = <make_{{service:rust_name}} as ::fbthrift::ClientFactory>::Api;
pub type {{service:rust_name}}Client = ::std::sync::Arc<{{service:rust_name}}DynClient>;

/// The same thing, but to be called from generic contexts where we are
/// working with a type parameter `C: ClientFactory` to produce clients.
impl ::fbthrift::ClientFactory for make_{{service:rust_name}} {
    type Api = dyn {{service:rust_name}} + ::std::marker::Send + ::std::marker::Sync + 'static;

    fn with_spawner<P, T, S>(protocol: P, transport: T, spawner: S) -> ::std::sync::Arc<Self::Api>
    where
        P: ::fbthrift::Protocol<Frame = T>,
        T: ::fbthrift::Transport,
        P::Deserializer: ::std::marker::Send,
        S: ::fbthrift::help::Spawner,
    {
        <dyn {{service:rust_name}}>::with_spawner(protocol, transport, spawner)
    }
}
{{/service:interaction?}}

{{!newline}}
